#include "js-engine.h"
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <string.h>
#include <sys/time.h>
#include <libhal.h>
#include <dbus/dbus.h>

#define UDI_DEVICES_COMPUTER "/org/freedesktop/Hal/devices/computer"
#define NOTIMPLEMENTED "not implemented"

struct prop_dictionary {
    const gchar *prop;
    const gchar *udi;
    const gchar *key;
    jsval (*getter)(JSContext *cx, const gchar *udi, const gchar *key);
};

static DBusConnection *conn = NULL;
static LibHalContext *halcx = NULL;
static JSBool audio_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
static JSBool graphic_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
static JSBool bios_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
static JSBool cursor_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
static JSBool machine_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
static JSBool memory_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
static JSBool network_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
static JSBool wireless_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
static JSBool wirelessaccesspoint_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
static JSBool perfmon_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
static JSBool power_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
static JSBool process_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
static JSBool processor_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
static JSBool screen_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);

static jsval get_hal_jsval_string(JSContext *cx, const gchar *udi, const gchar *key);
static jsval get_hal_jsval_integer(JSContext *cx, const gchar *udi, const gchar *key);
static jsval get_hal_jsval_number(JSContext *cx, const gchar *udi, const gchar *key);
static jsval get_hal_jsval_boolean(JSContext *cx, const gchar *udi, const gchar *key);
static jsval get_hal_jsval_stub(JSContext *cx, const gchar *udi, const gchar *key);

static JSClass frameworkClass = 
    {"framework", JSCLASS_HAS_PRIVATE,
     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, 
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
     JSCLASS_NO_OPTIONAL_MEMBERS
    };

static JSClass audioClass = 
    {"audio", 0,
     JS_PropertyStub, JS_PropertyStub, audio_GetProperty, JS_PropertyStub, 
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
     JSCLASS_NO_OPTIONAL_MEMBERS
    };

static JSClass graphicsClass = 
    {"graphics", 0,
     JS_PropertyStub, JS_PropertyStub, graphic_GetProperty, JS_PropertyStub, 
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
     JSCLASS_NO_OPTIONAL_MEMBERS
    };

static JSClass systemClass = 
    {"system", 0,
     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, 
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
     JSCLASS_NO_OPTIONAL_MEMBERS
    };

static JSClass biosClass =
    {"bios", 0,
     JS_PropertyStub, JS_PropertyStub, bios_GetProperty, JS_PropertyStub, 
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
     JSCLASS_NO_OPTIONAL_MEMBERS
    };

static JSClass cursorClass =
    {"cursor", 0,
     JS_PropertyStub, JS_PropertyStub, cursor_GetProperty, JS_PropertyStub, 
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
     JSCLASS_NO_OPTIONAL_MEMBERS
    };

static JSClass machineClass =
    {"machine", 0,
     JS_PropertyStub, JS_PropertyStub, machine_GetProperty, JS_PropertyStub, 
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
     JSCLASS_NO_OPTIONAL_MEMBERS
    };

static JSClass memoryClass =
    {"memory", 0,
     JS_PropertyStub, JS_PropertyStub, memory_GetProperty, JS_PropertyStub, 
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
     JSCLASS_NO_OPTIONAL_MEMBERS
    };

static JSClass networkClass =
    {"network", 0,
     JS_PropertyStub, JS_PropertyStub, network_GetProperty, JS_PropertyStub, 
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
     JSCLASS_NO_OPTIONAL_MEMBERS
    };

static JSClass wirelessClass =
    {"wireless", 0,
     JS_PropertyStub, JS_PropertyStub, wireless_GetProperty, JS_PropertyStub, 
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
     JSCLASS_NO_OPTIONAL_MEMBERS
    };

static JSClass wirelessaccesspointClass =
    {"wirelessaccesspoint", 0,
     JS_PropertyStub, JS_PropertyStub, wirelessaccesspoint_GetProperty, JS_PropertyStub, 
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
     JSCLASS_NO_OPTIONAL_MEMBERS
    };

static JSClass perfmonClass =
    {"perfmon", 0,
     JS_PropertyStub, JS_PropertyStub, perfmon_GetProperty, JS_PropertyStub, 
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
     JSCLASS_NO_OPTIONAL_MEMBERS
    };

static JSClass powerClass =
    {"power", 0,
     JS_PropertyStub, JS_PropertyStub, power_GetProperty, JS_PropertyStub, 
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
     JSCLASS_NO_OPTIONAL_MEMBERS
    };

static JSClass processClass =
    {"process", 0,
     JS_PropertyStub, JS_PropertyStub, process_GetProperty, JS_PropertyStub, 
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
     JSCLASS_NO_OPTIONAL_MEMBERS
    };

static JSClass processorClass =
    {"processor", 0,
     JS_PropertyStub, JS_PropertyStub, processor_GetProperty, JS_PropertyStub, 
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
     JSCLASS_NO_OPTIONAL_MEMBERS
    };

static JSClass screenClass =
    {"screen", 0,
     JS_PropertyStub, JS_PropertyStub, screen_GetProperty, JS_PropertyStub, 
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
     JSCLASS_NO_OPTIONAL_MEMBERS
    };

static JSBool framework_browseForFile(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval);
static JSBool framework_browseForFiles(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval);

static JSBool system_getFileIcon(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval);
static JSBool system_languageCode(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval);
static JSBool system_localTimeToUniversalTime(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval);


static JSFunctionSpec framework_methods[] = {
    {"BrowseForFile", framework_browseForFile, 1, 0, 0},
    {"BrowseForFiles", framework_browseForFiles, 1, 0, 0},
    {NULL, NULL, 0, 0, 0}
};

static JSFunctionSpec system_methods[] = {
    {"getFileIcon", system_getFileIcon, 1, 0, 0},
    {"languageCode", system_languageCode, 0, 0, 0},
    {"localTimeToUniversalTime", system_localTimeToUniversalTime, 1, 0, 0},
    {NULL, NULL, 0, 0, 0}
};

static void set_filefilter(GtkFileChooser *chooser, const gchar *filter_arg)
{
    GtkFileFilter *filter = gtk_file_filter_new();
    gchar **filters = g_strsplit(filter_arg,"|", 0);
    gchar **filters_ptr = filters;
    while(*filters_ptr){
	gchar **fileTypes, **fileTypes_ptr;
	if(*filters_ptr++ == NULL)
	    break;
	fileTypes = g_strsplit(*filters_ptr++, ";", 0);
	fileTypes_ptr = fileTypes;
	while(*fileTypes_ptr){
	    gtk_file_filter_add_pattern(filter, *fileTypes_ptr++);
	}
	g_strfreev(fileTypes);
    }
    gtk_file_chooser_set_filter(chooser, filter);
    g_strfreev(filters);
}

static JSBool framework_browseForFile(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval)
{
    gint result;
    gchar *filename = NULL;
    GtkWidget *dialog;

    dialog = gtk_file_chooser_dialog_new (_("Open File"), NULL,
					  GTK_FILE_CHOOSER_ACTION_OPEN,
					  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
					  GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
					  NULL);
    gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
    if(argc >= 1 && JSVAL_IS_STRING(args[0]))
	set_filefilter(GTK_FILE_CHOOSER(dialog), JS_GetStringBytes(JSVAL_TO_STRING(args[0])));

    result = gtk_dialog_run(GTK_DIALOG(dialog));
    if(result == GTK_RESPONSE_ACCEPT){
	JSString *str;
	filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
	g_message(filename);
	str = JS_NewStringCopyZ(cx, filename);
	*rval = STRING_TO_JSVAL(str);
	g_free(filename);
    }else{
	*rval = JSVAL_VOID;
    }
    gtk_widget_destroy(dialog);
    return JS_TRUE;
}

static JSBool framework_browseForFiles(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval)
{
    gint result;
    GtkWidget *dialog;

    dialog = gtk_file_chooser_dialog_new (_("Open File"), NULL,
					  GTK_FILE_CHOOSER_ACTION_OPEN,
					  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
					  GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
					  NULL);
    gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
    if(argc >= 1 && JSVAL_IS_STRING(args[0]))
	set_filefilter(GTK_FILE_CHOOSER(dialog), JS_GetStringBytes(JSVAL_TO_STRING(args[0])));

    result = gtk_dialog_run(GTK_DIALOG(dialog));
    if(result == GTK_RESPONSE_ACCEPT){
	GSList *files = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
	JSObject *array = JS_NewArrayObject(cx, g_slist_length(files), NULL);
	int i;
	for(i = 0; i < g_slist_length(files); i++){
	    gchar *filename = g_slist_nth_data(files, i);
	    jsval str = STRING_TO_JSVAL
		(JS_NewStringCopyZ(cx, filename));
	    g_free(filename);
	    JS_SetElement(cx, array, i, &str);
	}
	g_slist_free(files);
	*rval = OBJECT_TO_JSVAL(array);
    }else{
	*rval = JSVAL_VOID;
    }
    gtk_widget_destroy(dialog);
    return JS_TRUE;
}

static JSBool system_getFileIcon(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval)
{
    g_warning(NOTIMPLEMENTED);
    return JS_TRUE;
}

static JSBool system_languageCode(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval)
{
    const gchar *lang = g_get_language_names()[0];
    if(lang){
	gchar **parsed = g_strsplit(lang, "_", 2);
	if(strcmp(*parsed, "C")){
	    *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, *parsed));
	}else{
	    *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, "en"));
	}
	g_strfreev(parsed);
    }else{
	*rval = JSVAL_VOID;
    }
    return JS_TRUE;
}

static JSBool system_localTimeToUniversalTime(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval)
{
    g_warning(NOTIMPLEMENTED);
    return JS_TRUE;
}

static jsval get_hal_jsval_string(JSContext *cx, const gchar *udi, const gchar *key)
{
    gchar *value;
    jsval result = JSVAL_VOID;
    DBusError err;
    dbus_error_init(&err);
    value = libhal_device_get_property_string(halcx, udi, key, &err);
    if(err.message == NULL){
	result = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, value));
    }
    dbus_error_free(&err);
    return result;
}

static jsval get_hal_jsval_integer(JSContext *cx, const gchar *udi, const gchar *key)
{
    jsval result = JSVAL_VOID;
    gint value;
    DBusError err;
    dbus_error_init(&err);

    value = libhal_device_get_property_int(halcx, udi, key, &err);
    if(err.message == NULL){
	result = INT_TO_JSVAL(value);
    }
    dbus_error_free(&err);
    return result;
}

static jsval get_hal_jsval_boolean(JSContext *cx, const gchar *udi, const gchar *key)
{
    dbus_bool_t value;
    jsval result = JSVAL_VOID;
    DBusError err;
    dbus_error_init(&err);

    value = libhal_device_get_property_bool(halcx, udi, key, &err);
    if(err.message == NULL){
	result = BOOLEAN_TO_JSVAL(value);
    }
    dbus_error_free(&err);
    return result;
}

static jsval get_hal_jsval_number(JSContext *cx, const gchar *udi, const gchar *key)
{
    gdouble value;
    jsval result = JSVAL_VOID;
    DBusError err;
    dbus_error_init(&err);

    value = libhal_device_get_property_double(halcx, udi, key, &err);
    if(err.message == NULL){
	JS_NewNumberValue(cx, value, &result);
    }
    dbus_error_free(&err);
    return result;
}

static jsval get_hal_jsval_stub(JSContext *cx, const gchar *udi, const gchar *key)
{
    g_warning("property \"%s\" is not implemented",key);
    return JSVAL_VOID;
}

static JSBool audio_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    g_warning(NOTIMPLEMENTED);
    return JS_TRUE;
}

static JSBool graphic_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    g_warning(NOTIMPLEMENTED);
    return JS_TRUE;
}

static JSBool bios_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    struct prop_dictionary list[] = {
	{"serialNumber", UDI_DEVICES_COMPUTER, "system.hardware.serial", get_hal_jsval_string}, 
	{NULL}
    };
    struct prop_dictionary *ptr = list;
    const gchar *prop = JS_GetStringBytes(JSVAL_TO_STRING(id));

    while(ptr->prop){
	if(g_ascii_strcasecmp(prop, ptr->prop) == 0){
	    *vp = ptr->getter(cx, ptr->udi, ptr->key);
	    break;
	}
	ptr++;
    }
    return JS_TRUE;
}

static JSBool cursor_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    g_warning(NOTIMPLEMENTED);
    return JS_TRUE;
}

static JSBool machine_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    struct prop_dictionary list[] = {
	{"manufacturer", UDI_DEVICES_COMPUTER, "smbios.system.manufacturer", get_hal_jsval_string}, 
	{"model", UDI_DEVICES_COMPUTER, "system.hardware.product", get_hal_jsval_string},
	{NULL}
    };
    struct prop_dictionary *ptr = list;
    const gchar *prop = JS_GetStringBytes(JSVAL_TO_STRING(id));

    while(ptr->prop){
	if(g_ascii_strcasecmp(prop, ptr->prop) == 0){
	    *vp = ptr->getter(cx, ptr->udi, ptr->key);
	    break;
	}
	ptr++;
    }
    return JS_TRUE;
}

static JSBool memory_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    const gchar *key = JS_GetStringBytes(JSVAL_TO_STRING(id));
    struct prop_dictionary list[] = {
	{"free", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"total", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"used", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"freePhysical", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"totalPhysical", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"usedPhysical", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
    };
    struct prop_dictionary *ptr = list;
    while(ptr->prop){
	if(g_ascii_strcasecmp(key, ptr->prop) == 0){
	    *vp = ptr->getter(cx, ptr->udi, ptr->key);
	    break;
	}
	ptr++;
    }
    return JS_TRUE;

}
    
static JSBool network_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    const gchar *key = JS_GetStringBytes(JSVAL_TO_STRING(id));
    struct prop_dictionary list[] = {
	{"online", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"connectionType", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"physicalMediaType", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"wireless", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
    };
    struct prop_dictionary *ptr = list;
    while(ptr->prop){
	if(g_ascii_strcasecmp(key, ptr->prop) == 0){
	    *vp = ptr->getter(cx, ptr->udi, ptr->key);
	    break;
	}
	ptr++;
    }
    return JS_TRUE;
}

static JSBool wireless_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    const gchar *key = JS_GetStringBytes(JSVAL_TO_STRING(id));
    struct prop_dictionary list[] = {
	{"available", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"connected", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"enumerateAvailableAccessPoints", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"enumerationSupported", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"name", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"networkName", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"signalStrength", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
    };
    struct prop_dictionary *ptr = list;
    while(ptr->prop){
	if(g_ascii_strcasecmp(key, ptr->prop) == 0){
	    *vp = ptr->getter(cx, ptr->udi, ptr->key);
	    break;
	}
	ptr++;
    }
    return JS_TRUE;
}

static JSBool wirelessaccesspoint_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    const gchar *key = JS_GetStringBytes(JSVAL_TO_STRING(id));
    struct prop_dictionary list[] = {
	{"name", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"type", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"signalStrength", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
    };
    struct prop_dictionary *ptr = list;
    while(ptr->prop){
	if(g_ascii_strcasecmp(key, ptr->prop) == 0){
	    *vp = ptr->getter(cx, ptr->udi, ptr->key);
	    break;
	}
	ptr++;
    }
    return JS_TRUE;
}

static JSBool perfmon_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    g_warning(NOTIMPLEMENTED);
    return JS_TRUE;
}

static JSBool power_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    struct prop_dictionary list[] = {
	{"charging", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"percentRemaining", UDI_DEVICES_COMPUTER, "battery.charge_level.percentage", get_hal_jsval_integer},
	{"pluggedIn", UDI_DEVICES_COMPUTER, "ac_adapter.present", get_hal_jsval_string},
	{"timeRemaining", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"timeTotal", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{NULL}
    };
    struct prop_dictionary *ptr = list;
    const gchar *key = JS_GetStringBytes(JSVAL_TO_STRING(id));    

    while(ptr->prop){
	if(g_ascii_strcasecmp(key, ptr->prop) == 0){
	    *vp = ptr->getter(cx, ptr->udi, ptr->key);
	    break;
	}
	ptr++;
    }
    return JS_TRUE;
}

static JSBool process_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    const gchar *key = JS_GetStringBytes(JSVAL_TO_STRING(id));
    struct prop_dictionary list[] = {
	{"enumerateProcesses", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"foreground", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
    };
    struct prop_dictionary *ptr = list;
    while(ptr->prop){
	if(g_ascii_strcasecmp(key, ptr->prop) == 0){
	    *vp = ptr->getter(cx, ptr->udi, ptr->key);
	    break;
	}
	ptr++;
    }
    return JS_TRUE;
}

static JSBool screen_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    g_warning(NOTIMPLEMENTED);
    return JS_TRUE;
}

static JSBool processor_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    const gchar *key = JS_GetStringBytes(JSVAL_TO_STRING(id));
    struct prop_dictionary list[] = {
	{"architecture", UDI_DEVICES_COMPUTER, "system.kernel.machine", get_hal_jsval_string},
	{"count", "/org/freedesktop/Hal/devices/acpi_P001", "processor.number", get_hal_jsval_integer},
	{"family", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"name", "/org/freedesktop/Hal/devices/acpi_P001", "info.product", get_hal_jsval_string},
	{"speed", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"stepping", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{"vendor", UDI_DEVICES_COMPUTER, NULL, get_hal_jsval_stub},
	{NULL}
    };

    struct prop_dictionary *ptr = list;
    while(ptr->prop){
	if(g_ascii_strcasecmp(key, ptr->prop) == 0){
	    *vp = ptr->getter(cx, ptr->udi, ptr->key);
	    break;
	}
	ptr++;
    }
    if(g_strcasecmp("count", key) == 0 && JSVAL_IS_INT(*vp)){
	*vp = INT_TO_JSVAL(JSVAL_TO_INT(*vp)+1);
    }

    return JS_TRUE;
}

JSBool frameworkClass_init(js_engine *engine)
{
    JSObject *framework, *audio, *graphics, *system,
	*bios, *cursor,	*machine, *memory, *network, 
	*wireless, *wirelessaccesspoint, *perfmon, *power,
	*process, *processor, *screen;
    jsval val;
    DBusError err;
    
    dbus_error_init(&err);
    conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
    if(!conn){
	g_warning(err.message);
    }
    halcx = libhal_ctx_new();
    libhal_ctx_set_dbus_connection(halcx, conn);
    libhal_ctx_init(halcx, &err);
    dbus_error_free(&err);

    framework = JS_DefineObject(engine->cx, engine->glob, "framework", &frameworkClass, NULL, JSPROP_PERMANENT);

    g_return_val_if_fail(framework, JS_FALSE);
    g_return_val_if_fail(JS_DefineFunctions(engine->cx, framework, framework_methods), JS_FALSE);
    audio = JS_NewObject(engine->cx, &audioClass, NULL, NULL);
    val = OBJECT_TO_JSVAL(audio);
    g_return_val_if_fail(JS_SetProperty(engine->cx, framework, "audio", &val), JS_FALSE);
    g_return_val_if_fail(JS_SetProperty(engine->cx, engine->glob, "audio", &val), JS_FALSE);

    graphics = JS_NewObject(engine->cx, &graphicsClass, NULL, NULL);
    val = OBJECT_TO_JSVAL(graphics);
    g_return_val_if_fail(JS_SetProperty(engine->cx, framework, "graphics", &val), JS_FALSE);
    g_return_val_if_fail(JS_SetProperty(engine->cx, engine->glob, "graphics", &val), JS_FALSE);

    system = JS_NewObject(engine->cx, &systemClass, NULL, NULL);
    val = OBJECT_TO_JSVAL(system);
    g_return_val_if_fail(JS_SetProperty(engine->cx, framework, "system", &val), JS_FALSE);
    g_return_val_if_fail(JS_SetProperty(engine->cx, engine->glob, "system", &val), JS_FALSE);
    g_return_val_if_fail(JS_DefineFunctions(engine->cx, system, system_methods), JS_FALSE);
    
    bios = JS_NewObject(engine->cx, &biosClass, NULL, NULL);
    val = OBJECT_TO_JSVAL(bios);
    g_return_val_if_fail(JS_SetProperty(engine->cx, system, "bios", &val), JS_FALSE);

    cursor = JS_NewObject(engine->cx, &cursorClass, NULL, NULL);
    val = OBJECT_TO_JSVAL(cursor);
    g_return_val_if_fail(JS_SetProperty(engine->cx, system, "cursor", &val), JS_FALSE);

    machine = JS_NewObject(engine->cx, &machineClass, NULL, NULL);
    val = OBJECT_TO_JSVAL(machine);
    g_return_val_if_fail(JS_SetProperty(engine->cx, system, "machine", &val), JS_FALSE);
    
    memory = JS_NewObject(engine->cx, &memoryClass, NULL, NULL);
    val = OBJECT_TO_JSVAL(memory);
    g_return_val_if_fail(JS_SetProperty(engine->cx, system, "memory", &val), JS_FALSE);

    network = JS_NewObject(engine->cx, &networkClass, NULL, NULL);
    val = OBJECT_TO_JSVAL(network);
    g_return_val_if_fail(JS_SetProperty(engine->cx, system, "network", &val), JS_FALSE);

    wireless = JS_NewObject(engine->cx, &wirelessClass, NULL, NULL);
    val = OBJECT_TO_JSVAL(wireless);
    g_return_val_if_fail(JS_SetProperty(engine->cx, network, "wireless", &val), JS_FALSE);

    wirelessaccesspoint = JS_NewObject(engine->cx, &wirelessaccesspointClass, NULL, NULL);
    val = OBJECT_TO_JSVAL(wirelessaccesspoint);
    g_return_val_if_fail(JS_SetProperty(engine->cx, network, "wirelessaccesspoint", &val), JS_FALSE);

    perfmon = JS_NewObject(engine->cx, &perfmonClass, NULL, NULL);
    val = OBJECT_TO_JSVAL(perfmon);
    g_return_val_if_fail(JS_SetProperty(engine->cx, system, "perfmon", &val), JS_FALSE);

    power = JS_NewObject(engine->cx, &powerClass, NULL, NULL);
    val = OBJECT_TO_JSVAL(power);
    g_return_val_if_fail(JS_SetProperty(engine->cx, system, "power", &val), JS_FALSE);

    process = JS_NewObject(engine->cx, &processClass, NULL, NULL);
    val = OBJECT_TO_JSVAL(process);
    g_return_val_if_fail(JS_SetProperty(engine->cx, system, "process", &val), JS_FALSE);

    processor = JS_NewObject(engine->cx, &processorClass, NULL, NULL);
    val = OBJECT_TO_JSVAL(processor);
    g_return_val_if_fail(JS_SetProperty(engine->cx, system, "processor", &val), JS_FALSE);

    screen = JS_NewObject(engine->cx, &screenClass, NULL, NULL);
    val = OBJECT_TO_JSVAL(screen);
    g_return_val_if_fail(JS_SetProperty(engine->cx, system, "screen", &val), JS_FALSE);

    return JS_TRUE;
}

JSBool frameworkClass_shutdown(void)
{
    DBusError err;
    dbus_error_init(&err);
    libhal_ctx_shutdown(halcx, &err);
    libhal_ctx_free(halcx);
    dbus_error_free(&err);
    dbus_connection_unref(conn);
    
    return JS_TRUE;
}
